#include "hefeng_weather_data.hpp"
#include <any>
#include <cstddef>
#include <string>
#include <tuple>
#include <vector>
#include "content/data_provider.hpp"
#include "fmt/format.h"
#include "service/http_service.hpp"
#include "service/render_service.hpp"
#include "service/svc_manager.hpp"
#include "core/log.hpp"
#include "job/js.hpp"
#include "json/json.hpp"
#include "icon_manager.hpp"

HefengData::HefengData(std::string apikey, std::string *city_id, IconManager *mmg): apikey(apikey), current_city_id(city_id), icons_(mmg) {
    RESOURCE_VIEW(realtime_, "/layouts/real_time_weather.xml");
    RESOURCE_VIEW(find_, "/layouts/find_city.xml");

    selected_result = -1;
    result_count.value = 0;
}

HefengData::~HefengData() {
    delete realtime_;
    delete find_;
}
/*
std::optional<std::tuple<value_type, std::any>> HefengData::get_data(const std::string &key)
{
    if (key=="real_time_weather") {
        return std::make_tuple(value_type::UI, std::any{&realtime_});
    }
    if (key=="find_city") {
        return std::make_tuple(value_type::UI, std::any{&find_});
    }
    if (key=="city_search_keyword") {
        return std::make_tuple(value_type::STD_STRING, std::any{&search_keyword});
    }
    if (key=="do_search") {
        callback_t ret{
            .action = +[](void* p){ ((HefengData*)p)->do_search(); },
            .user_data = this,
        };
        return std::make_tuple(value_type::CALLBACK, std::any{ret});
    }
    if (key=="selected_result") {
        return std::make_tuple(value_type::U32, std::any{&selected_result});
    }
    if (key=="result_list") {
        enumerate_t ret {
            .count = &result_count,
            .text = +[](void *p, u32 idx)->const char* { return ((HefengData*)p)->city_results[idx].display.c_str(); },
            .user_data = this,
        };
        return std::make_tuple(value_type::ENUMERATE, std::any{ret});
    }
    if (key=="do_choose_city") {
        callback_t ret{
            .action = +[](void* p){ ((HefengData*)p)->do_choose_city(); },
            .user_data = this,
        };
        return std::make_tuple(value_type::CALLBACK, std::any{ret});
    }
    if (key=="weather_icon") {
        return std::make_tuple(value_type::IMAGE, std::any{&weather_icon});
    }
    if (key=="weather_text") {
        return std::make_tuple(value_type::STD_STRING, std::any{&weather_info.text});
    }
    if (key=="weather_humidity") {
        return std::make_tuple(value_type::STD_STRING, std::any{&weather_info.humidity});
    }
    if (key=="weather_feelsLike") {
        return std::make_tuple(value_type::STD_STRING, std::any{&weather_info.feelsLike});
    }
    if (key=="weather_wind") {
        return std::make_tuple(value_type::STD_STRING, std::any{&weather_info.wind});
    }
    if (key=="weather_temp") {
        return std::make_tuple(value_type::STD_STRING, std::any{&weather_info.temp});
    }
    if (key=="do_update_weather") {
        callback_t ret{
            .action = +[](void* p){ ((HefengData*)p)->do_update_weather(); },
            .user_data = this,
        };
        return std::make_tuple(value_type::CALLBACK, std::any{ret});
    }
    return {};
}
*/
void HefengData::do_search() {
    if (!search_keyword.empty()) {
        L_INFO("Search city for %s", search_keyword.c_str());

        selected_result = -1;
        result_count.value = 0;

        auto exec = [this, kw = search_keyword]{
            auto response = ServiceManager::instance()->request_service<IHttpService>()
            ->new_get("https://geoapi.qweather.com/v2/city/lookup")
            ->add_param("location", kw)
            ->add_param("key", apikey)
            ->add_param("range", "cn")
            ->add_param("number", "20")
            ->add_param("lang", "zh")
            ->ssl(false)
            ->perform();

            // L_INFO("%s", response.c_str());

            using namespace nlohmann;

            auto doc = json::parse(response, nullptr, false);
            if (doc.is_object()) {
                std::string code = doc.value("code", "");
                if (code == "200") {
                    // ok, parse city list
                    std::vector<CityResult> results;
                    auto cities = doc["location"];
                    for (auto &city: cities) {
                        CityResult item;
                        item.display = fmt::format("{},{},{},{}",
                         city.value("country","-"),
                          city.value("adm1","-"),
                          city.value("adm2","-"),
                          city.value("name","-")
                          );
                        item.id = city.value("id","");
                        results.emplace_back(std::move(item));
                    }
                    std::swap(city_results, results);
                    selected_result = 0;
                    result_count.value = (u32)city_results.size();
                }
            }
        };

        submit_job(create_job(exec));
    }
}

void HefengData::do_choose_city() {
    if (selected_result < city_results.size()) {
        L_INFO("select is %s(id: %s)", city_results[selected_result].display.c_str(), city_results[selected_result].id.c_str());
        *current_city_id = city_results[selected_result].id;
    }
}

void HefengData::do_update_weather()
{
    if (current_city_id->empty()) {
        return;
    }

    auto exec = [this, id = *current_city_id]{
        auto response = ServiceManager::instance()->request_service<IHttpService>()
            ->new_get("https://devapi.qweather.com/v7/weather/now")
            ->add_param("location", id)
            ->add_param("key", apikey)
            ->add_param("lang", "zh")
            ->ssl(false)
            ->perform();
        
        using namespace nlohmann;

        auto doc = json::parse(response, nullptr, false);
        if (doc.is_object()) {
            std::string code = doc.value("code", "");
                if (code == "200") {
                    // ok, parse city list
                    RealtimeWeatherInfo info;
                    auto now = doc["now"];
                    info.temp = now.value("temp", "N/A");
                    info.feelsLike = now.value("feelsLike", "N/A");
                    info.icon = now.value("icon", "N/A");
                    info.text = now.value("text", "N/A");
                    info.wind = fmt::format("{},{}级,{}公里/小时", now.value("windDir", "N/A"),now.value("windScale", "N/A"),now.value("windSpeed", "N/A"));
                    info.humidity = now.value("humidity", "N/A");
                    weather_icon = icons_->icon(info.icon.c_str());
                    std::swap(info, weather_info);
                }
        }
    };

    submit_job(create_job(exec));
}

bindable_base::binddata_t HefengData::post_get (const std::string &key, bool *handled)
{
    bindable_base::binddata_t r;
    if (key=="city_search_keyword") {
        r = std::make_tuple(value_type::STD_STRING, std::any{&search_keyword});
    }
    else if (key=="do_search") {
        callback_t ret{
                .action = +[](void* p){ ((HefengData*)p)->do_search(); },
                .user_data = this,
        };
        r = std::make_tuple(value_type::CALLBACK, std::any{ret});
    }
    else if (key=="selected_result") {
        r = std::make_tuple(value_type::U32, std::any{&selected_result});
    }
    else if (key=="result_list") {
        enumerate_t ret {
                .count = &result_count,
                .text = +[](void *p, u32 idx)->const char* { return ((HefengData*)p)->city_results[idx].display.c_str(); },
                .user_data = this,
        };
        r = std::make_tuple(value_type::ENUMERATE, std::any{ret});
    }
    else if (key=="do_choose_city") {
        callback_t ret{
                .action = +[](void* p){ ((HefengData*)p)->do_choose_city(); },
                .user_data = this,
        };
        r = std::make_tuple(value_type::CALLBACK, std::any{ret});
    }
    else if (key=="weather_icon") {
        r = std::make_tuple(value_type::IMAGE, std::any{&weather_icon});
    }
    else if (key=="weather_text") {
        r = std::make_tuple(value_type::STD_STRING, std::any{&weather_info.text});
    }
    else if (key=="weather_humidity") {
        r = std::make_tuple(value_type::STD_STRING, std::any{&weather_info.humidity});
    }
    else if (key=="weather_feelsLike") {
        r = std::make_tuple(value_type::STD_STRING, std::any{&weather_info.feelsLike});
    }
    else if (key=="weather_wind") {
        r = std::make_tuple(value_type::STD_STRING, std::any{&weather_info.wind});
    }
    else if (key=="weather_temp") {
        r = std::make_tuple(value_type::STD_STRING, std::any{&weather_info.temp});
    }
    else if (key=="do_update_weather") {
        callback_t ret{
                .action = +[](void* p){ ((HefengData*)p)->do_update_weather(); },
                .user_data = this,
        };
        r = std::make_tuple(value_type::CALLBACK, std::any{ret});
    }
    *handled = r.has_value();
    return r;
}

void Test::hello_world () {
    L_INFO ("Hello World!");
}
